home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Almathera Ten Pack 3: CDPD 3
/
Almathera Ten on Ten - Disc 3: CDPD3.iso
/
fish
/
676-700
/
697
/
hackdisk
/
hackdisk.s
< prev
next >
Wrap
Text File
|
1995-03-18
|
67KB
|
2,972 lines
********************************************************************************
* hackdisk.s -- my very own version of trackdisk.device
* Copyright (C) 1992 by Dan Babcock
*
* Version history:
* V1.00 04/25/92 First version. Supports 880K drives only.
* V1.01 04/28/92 Optimized CopyMem
* V1.02 04/30/92 More intelligent write algorithm fixes performance problem
* V1.03 05/03/92 Sets IO_ACTUAL
* V1.04 06/24/92 Fixes a couple bugs: RemChangeInt works now, and disk change
* errors are only reported when it makes sense.
* CrossDOSV5/CrossPC flushed out these bugs.
* V1.10 07/04/92 Compatible with Kickstart 1.2 and higher
* Fixed Seek (IO_OFFSET is in bytes, not tracks)
********************************************************************************
;Standard register usage:
;A6 - ExecBase or $dff000
;A5 (A_DEVICE) - device ptr
;A4 (A_UNIT) - unit ptr
;A3 (A_IO) - IORequest
exeobj
multipass
;There are supposed to be two modes of operation, the RamKick (ROM) mode
;and the DEVS:/mountlist (DISK) mode. However, the disk mode is only
;half-implemented, because I decided at some point it was useless.
ROM equ 1
DISK equ 2 ;NOTE: Not currently functioning!
TYPE equ ROM
;Set INFO_LEVEL to 1 for full debugging output over the internal serial port.
INFO_LEVEL equ 0
objfile 'devs:hackdisk.device'
A_DEVICE equr a5
A_UNIT equr a4
A_IO equr a3
VERSION equ 127
REVISION equ 10
MYPRI equ 0
MD_NUMUNITS equ 4
STACKSIZE equ 4000
TASKPRI equ 5
RETRYCNT equ 3
;Put a message to the serial port. Used like so:
;
;PUTDEBUG <'Init: called'>
;
;Parameters can be printed out by pushing them on the stack and
;adding the appropriate C printf-style % formatting commands.
PUTDEBUG macro ;<msg>
ifne INFO_LEVEL
movem.l d0-d1/a0-a1,-(sp)
lea .msg\@(pc),a0 ;Point to static format string
lea 16(sp),a1 ;Point to args
bsr KPutFmt
movem.l (sp)+,d0-d1/a0-a1
bra .end\@
.msg\@:
dc.b \1,$a,0
even
.end\@:
endc
endm
*--------------------------------------------------------------------
*
* Driver error defines
*
*--------------------------------------------------------------------
;TDERR_NotSpecified EQU 20 ; general catchall
;TDERR_NoSecHdr EQU 21 ; couldn't even find a sector
;TDERR_BadSecPreamble EQU 22 ; sector looked wrong
;TDERR_BadSecID EQU 23 ; ditto
;TDERR_BadHdrSum EQU 24 ; header had incorrect checksum
;TDERR_BadSecSum EQU 25 ; data had incorrect checksum
;TDERR_TooFewSecs EQU 26 ; couldn't find enough sectors
;TDERR_BadSecHdr EQU 27 ; another "sector looked wrong"
;TDERR_WriteProt EQU 28 ; can't write to a protected disk
;TDERR_DiskChanged EQU 29 ; no disk in the drive
;TDERR_SeekError EQU 30 ; couldn't find track 0
;TDERR_NoMem EQU 31 ; ran out of memory
;TDERR_BadUnitNum EQU 32 ; asked for a unit > NUMUNITS
;TDERR_BadDriveType EQU 33 ; not a drive that trackdisk groks
;TDERR_DriveInUse EQU 34 ; someone else allocated the drive
;TDERR_PostReset EQU 35 ; user hit reset; awaiting doom
; STRUCTURE TIMEVAL,0
; ULONG TV_SECS
; ULONG TV_MICRO
; LABEL TV_SIZE
;Unit structure
*--------------------------------------------------------------------
*
* Public portion of unit structure
*
*--------------------------------------------------------------------
;*------ UNIT_FLAG definitions:
;These are bogus, but I won't re-define them.
; BITDEF UNIT,ACTIVE,0 ; driver is active
; BITDEF UNIT,INTASK,1 ; running in driver's task
BITDEF UNIT,DiskInDrive,2
BITDEF UNIT,WriteProtected,3
; STRUCTURE TDU_PUBLICUNIT,UNIT_SIZE
; UWORD TDU_COMP01TRACK ; track for first precomp
; UWORD TDU_COMP10TRACK ; track for second precomp
; UWORD TDU_COMP11TRACK ; track for third precomp
; ULONG TDU_STEPDELAY ; time to wait after stepping
; ULONG TDU_SETTLEDELAY ; time to wait after seeking
; UBYTE TDU_RETRYCNT ; # of times to retry
; UBYTE TDU_PUBFLAGS ; public flags, see below
; UWORD TDU_CURRTRK ; track heads are over
; (ONLY ACCESS WHILE UNIT IS STOPPED!)
; ULONG TDU_CALIBRATEDELAY ; time to wait after stepping
; for recalibrate
; ULONG TDU_COUNTER ; counter for disk changes
; (ONLY ACCESS WHILE UNIT IS STOPPED!)
; LABEL TDU_PUBLICUNITSIZE
;*--------------------------------------------------------------------
STRUCTURE MyUnit,TDU_PUBLICUNITSIZE
STRUCT Drive_LastCheck,TV_SIZE ;last time diskchange was checked
STRUCT ChangeIntList,MLH_SIZE ;must be initialized!!!
LONG TDRemoveInt
IFEQ TYPE-DISK
;not used by ROM driver
STRUCT ChangeInt,IS_SIZE
STRUCT TDPort,MP_SIZE
STRUCT TDIORequest,IOTD_SIZE
ENDC
BYTE UnitNum
BYTE MyUnit_Pad
;MUST be word-aligned!!!
LABEL MyUnit_Sizeof
;*
;* Flags for TDU_PUBFLAGS:
;*
; BITDEF TDP,NOCLICK,0 ; set to enable noclickstart
BITDEF TDP,VERIFY,1
;Device global structure -- accessed as positive offsets from device base
;Note: The stuff in LIB_SIZE is *required*. Anything else is up to you...
STRUCTURE DeviceGlobals,LIB_SIZE
STRUCT TaskPort,MP_SIZE ;MsgPort for task
STRUCT Task,TC_SIZE
STRUCT TaskStack,STACKSIZE
STRUCT TempTimeVal,TV_SIZE
LONG SegList ;not used for ROM version
STRUCT TimerIORequest,IOTV_SIZE
STRUCT TimerPort,MP_SIZE
STRUCT LastCheck,TV_SIZE ;last time diskchange was checked
STRUCT DiskResourceUnit,DRU_SIZE ;must be initialized!!!
STRUCT DiskResourcePort,MP_SIZE ;must be initialized!!!
;Allocated memory pointers
LONG RawBuffer
LONG DecodedBuffer
LONG VerifyBuffer
STRUCT SectorLabels,11*16
STRUCT Unit0,MyUnit_Sizeof
STRUCT Unit1,MyUnit_Sizeof
STRUCT Unit2,MyUnit_Sizeof
STRUCT Unit3,MyUnit_Sizeof
LONG GraphBase
LONG IntBase
LONG DiskResourceBase
LONG CIABase
WORD SyncCount
WORD IndexDskLen
LONG WriteMap
LABEL StartSigs
LONG SyncSig
LONG BlockSig
LABEL EndSigs
BYTE BufferTrack
BYTE BufferDrive
BYTE DEV_FLAGS
BYTE MotorState
BYTE InquireBits
LABEL MyDev_Sizeof
NumSigs equ (EndSigs-StartSigs)/4
;Bit definitions for DEV_FLAGS:
BITDEF DEV,Dirty,0 ;buffered track has been changed
BITDEF DEV,Stopped,1 ;device is stopped
BITDEF DEV,Verify,2 ;used by BlockInt for verify
; BITDEF DEV,Test,3 ;for debugging only
IFEQ TYPE-DISK
;The first executable location. This should return an error in case someone
;tried to run us as a program (instead of loading us as a device).
moveq #-1,d0
rts
ENDC
;A romtag structure. After your driver is brought in from disk, the
;disk image will be scanned for this structure to discover magic constants
;about you (such as where to start running you from...).
RomTag:
dc.w RTC_MATCHWORD ;$4AFC ('illegal' opcode)
dc.l RomTag
dc.l EndCode ;pointer to end of code
dc.b RTF_AUTOINIT+RTF_COLDSTART ;set things up automatically
dc.b VERSION
dc.b NT_DEVICE ;module type (either device or library)
dc.b MYPRI ;usually not important
dc.l Name ;name used in OpenDevice
dc.l IDString ;optional
dc.l Init ;more init info
IFEQ TYPE-ROM
Name: dc.b 'trackdisk.device',0
even
ENDC
IFEQ TYPE-DISK
Name: dc.b 'hackdisk.device',0 ;this MUST be the same as the
even ;disk-resident name!
ENDC
IDString: dc.b 'Hackdisk V1.10 - Copyright (C) 1992 by Dan Babcock',$a,0
even
DiskResourceName:
dc.b 'disk.resource',0
TimerName:
dc.b 'timer.device',0
GraphName:
dc.b 'graphics.library',0
CIAName: dc.b 'ciab.resource',0
IntName: dc.b 'intuition.library',0
IFEQ TYPE-DISK
TDName: dc.b 'trackdisk.device',0
ENDC
even
;The romtag specified that we were "RTF_AUTOINIT". This means that the
;RT_INIT structure member points to one of these tables below. If the
;AUTOINIT bit was not set then RT_INIT would point to a routine to run.
Init: dc.l MyDev_Sizeof ;data space size (at least
;LIB_SIZE!!)
dc.l FuncTable ;pointer to function initializers
dc.l DataTable ;pointer to data initializers
dc.l InitRoutine ;routine to run
FuncTable:
dc.w -1 ;this indicates that the following are offsets,
;rather than absolute addresses
dc.w Open-FuncTable ;standard system routines
dc.w _Close-FuncTable
dc.w Expunge-FuncTable
dc.w .Null-FuncTable ;Reserved for future use!
dc.w BeginIO-FuncTable ;device definitions
dc.w AbortIO-FuncTable
dc.w -1 ;function table end marker
.Null: moveq #0,d0
rts
;The data table initializes static data structures. The format is
;specified in exec/InitStruct routine's manual pages. The
;INITBYTE/INITWORD/INITLONG macros are in the file "exec/initializers.i".
;The first argument is the offset from the device base for this
;byte/word/long. The second argument is the value to put in that cell.
;The table is null terminated
DataTable:
INITBYTE LN_TYPE,NT_DEVICE
INITLONG LN_NAME,Name
INITBYTE LIB_FLAGS,LIBF_SUMUSED+LIBF_CHANGED
INITWORD LIB_VERSION,VERSION
INITWORD LIB_REVISION,REVISION
INITLONG LIB_IDSTRING,IDString
dc.w 0 ;terminate list (only one byte needed)
CmdTable: dc.w Invalid-CmdTable ;0 CMD_INVALID
dc.w Invalid-CmdTable ;1 CMD_RESET
dc.w Read-CmdTable ;2 CMD_READ
dc.w Write-CmdTable ;3 CMD_WRITE / ETD_
dc.w Update-CmdTable ;4 CMD_UPDATE / ETD_
dc.w Clear-CmdTable ;5 CMD_CLEAR / ETD_
dc.w MyStop-CmdTable ;6 CMD_STOP / ETD_
dc.w Start-CmdTable ;7 CMD_START
dc.w Invalid-CmdTable ;8 CMD_FLUSH
dc.w Motor-CmdTable ;9 TD_MOTOR / ETD_
dc.w TDSeek-CmdTable ;10 TD_SEEK / ETD_
dc.w Format-CmdTable ;11 TD_FORMAT
dc.w TDRemove-CmdTable ;12 TD_REMOVE
dc.w ChangeNum-CmdTable ;13 TD_CHANGENUM
dc.w ChangeState-CmdTable ;14 TD_CHANGESTATE
dc.w ProtStatus-CmdTable ;15 TD_PROTSTATUS
dc.w TDRawRead-CmdTable ;16 TD_RAWREAD
dc.w RawWrite-CmdTable ;17 TD_RAWWRITE
dc.w GetDriveType-CmdTable ;18 TD_GETDRIVETYPE
dc.w GetNumTracks-CmdTable ;19 TD_GETNUMTRACKS
dc.w AddChangeInt-CmdTable ;20 TD_ADDCHANGEINT
dc.w RemChangeInt-CmdTable ;21 TD_REMCHANGEINT
dc.w GetGeometry-CmdTable ;22 TD_GETGEOMETRY
dc.w Invalid-CmdTable ;23 TD_EJECT
EndCmdTable:
HighestCommand equ ((EndCmdTable-CmdTable)/2)-1
Invalid: move.b #IOERR_NOCMD,IO_ERROR(A_IO)
rts
InitRoutine:
;A0 - segment
;D0 - device ptr
;Returns with the device ptr still in D0 if successful, otherwise zero.
PUTDEBUG <'InitRoutine: Entered'>
movem.l d1-d7/a0-a6,-(sp)
move.l d0,A_DEVICE
move.l a0,SegList(A_DEVICE)
move.l 4,a6
;Initialize ports
lea TaskPort(A_DEVICE),a0
bsr InitPort
lea TimerPort(A_DEVICE),a0
bsr InitPort
;Invalidate track buffer
bsr Clear
;Open libraries/resources
lea DiskResourceName(pc),a1 ;'disk.resource'
SYS OpenResource
move.l d0,DiskResourceBase(A_DEVICE)
beq .Failed
lea CIAName(pc),a1 ;'ciab.resource'
SYS OpenResource
move.l d0,CIABase(A_DEVICE)
beq .Failed
lea GraphName(pc),a1
SYS OldOpenLibrary
move.l d0,GraphBase(A_DEVICE)
beq .Failed
lea IntName(pc),a1
SYS OldOpenLibrary
move.l d0,IntBase(A_DEVICE)
beq .Failed
;Allocate CHIP memory. (Note: This will change when I support other drive
;types).
move.l #DISK_RawBufSize,d0
move.l #MEMF_CHIP,d1
SYS AllocMem
move.l d0,RawBuffer(A_DEVICE)
beq .Failed
move.l #512*11,d0
move.l #MEMF_CHIP,d1
SYS AllocMem
move.l d0,DecodedBuffer(A_DEVICE)
beq .Failed
move.l #(1088*11)+2,d0
move.l #MEMF_CHIP,d1
SYS AllocMem
move.l d0,VerifyBuffer(A_DEVICE)
beq .Failed
;Set up disk.resource structure.
lea DiskResourcePort(A_DEVICE),a0
bsr InitPort
move.l a0,DiskResourceUnit+MN_REPLYPORT(A_DEVICE)
; move.b #PA_SIGNAL,MP_FLAGS(a0) ;not needed (0)
lea Name(pc),a0
move.l a0,DiskResourceUnit+LN_NAME(A_DEVICE)
move.l A_DEVICE,DiskResourceUnit+DRU_DISCBLOCK+IS_DATA(A_DEVICE)
move.l A_DEVICE,DiskResourceUnit+DRU_DISCSYNC+IS_DATA(A_DEVICE)
move.l A_DEVICE,DiskResourceUnit+DRU_INDEX+IS_DATA(A_DEVICE)
lea BlockInt(pc),a0
move.l a0,DiskResourceUnit+DRU_DISCBLOCK+IS_CODE(A_DEVICE)
lea SyncInt(pc),a0
move.l a0,DiskResourceUnit+DRU_DISCSYNC+IS_CODE(A_DEVICE)
lea IndexInt(pc),a0
move.l a0,DiskResourceUnit+DRU_INDEX+IS_CODE(A_DEVICE)
moveq #NT_INTERRUPT,d0
move.b d0,DiskResourceUnit+DRU_DISCBLOCK+LN_TYPE(A_DEVICE)
move.b d0,DiskResourceUnit+DRU_DISCSYNC+LN_TYPE(A_DEVICE)
move.b d0,DiskResourceUnit+DRU_INDEX+LN_TYPE(A_DEVICE)
;Initialize timer IORequest (except signal)
lea TimerPort(A_DEVICE),a0
; move.b #PA_SIGNAL,MP_FLAGS(a0) ;not needed (0)
move.l a0,TimerIORequest+MN_REPLYPORT(A_DEVICE)
lea TimerName(pc),a0
moveq #UNIT_MICROHZ,d0
lea TimerIORequest(A_DEVICE),a1
moveq #0,d1
SYS OpenDevice
tst.l d0
bne .Failed
;*** Set up unit structures
moveq #0,d2
lea Unit0(A_DEVICE),A_UNIT
.UnitLoop:
IFEQ TYPE-DISK
lea TDPort(A_UNIT),a0
bsr InitPort
move.l a0,TDIORequest+MN_REPLYPORT(A_UNIT)
ENDC
move.b d2,UnitNum(A_UNIT)
lea ChangeIntList(A_UNIT),a0
NEWLIST a0
bset #1,TDU_PUBFLAGS(A_UNIT) ;verify ON!
moveq #-1,d0
move.w #80,TDU_COMP01TRACK(A_UNIT)
move.w d0,TDU_COMP10TRACK(A_UNIT)
move.w d0,TDU_COMP11TRACK(A_UNIT)
move.l #3*1000,TDU_STEPDELAY(A_UNIT)
move.l #15*1000,TDU_SETTLEDELAY(A_UNIT)
move.b #RETRYCNT,TDU_RETRYCNT(A_UNIT)
move.l #4*1000,TDU_CALIBRATEDELAY(A_UNIT)
lea MyUnit_Sizeof(A_UNIT),A_UNIT
addq.b #1,d2
cmp.b #MD_NUMUNITS,d2
blo .UnitLoop
;Set up task
move.b #PA_IGNORE,TaskPort+MP_FLAGS(A_DEVICE)
move.b #NT_MSGPORT,TaskPort+LN_TYPE(A_DEVICE)
lea Task(A_DEVICE),a1 ;Task Control Block
lea Name(pc),a0
move.l a0,LN_NAME(a1)
move.b #TASKPRI,LN_PRI(a1)
move.b #NT_TASK,LN_TYPE(a1)
lea TaskCode(pc),a2 ;initial PC
sub.l a3,a3 ;final PC
lea TaskStack(A_DEVICE),a0
move.l a0,TC_SPLOWER(a1)
lea STACKSIZE(a0),a0
move.l a0,TC_SPUPPER(a1)
move.l A_DEVICE,-(a0) ;pass device pointer to task
move.l a0,TC_SPREG(a1)
move.l a1,TaskPort+MP_SIGTASK(A_DEVICE)
SYS AddTask
move.l A_DEVICE,d0
PUTDEBUG <'InitRoutine: Done'>
.End: movem.l (sp)+,d1-d7/a0-a6
rts
.Failed:
PUTDEBUG <'InitRoutine: Failed'>
bsr CloseEverything
moveq #0,d0
bra .End
InitPort:
;Enter with pointer to port in A0. All registers preserved.
push a0
lea MP_MSGLIST(a0),a0
NEWLIST a0
pop a0
rts
CloseEverything:
;This routines cleans up device stuff. Enter with A_DEVICE.
movem.l d0-d1/a0-a1/a6,-(sp)
move.l 4,a6
;Close timer
tst.b TimerIORequest+IO_ERROR(A_DEVICE)
beq .SkipTimer
lea TimerIORequest(A_DEVICE),a1
SYS CloseDevice
.SkipTimer:
;Remove task
lea Name(pc),a1
SYS FindTask
tst.l d0
beq .SkipTask
move.l d0,a1
SYS RemTask
.SkipTask:
;Close libraries
move.l GraphBase(A_DEVICE),d0
beq .SkipGfx
move.l d0,a1
SYS CloseLibrary
.SkipGfx:
move.l IntBase(A_DEVICE),d0
beq .SkipInt
move.l d0,a1
SYS CloseLibrary
.SkipInt:
;Free CHIP RAM. (Note: This will change when I support other drive types).
move.l RawBuffer(A_DEVICE),d0
beq .SkipRaw
move.l d0,a1
move.l #DISK_RawBufSize,d0
SYS FreeMem
.SkipRaw:
move.l DecodedBuffer(A_DEVICE),d0
beq .SkipDecoded
move.l d0,a1
move.l #512*11,d0
SYS FreeMem
.SkipDecoded:
move.l VerifyBuffer(A_DEVICE),d0
beq .SkipVerify
move.l d0,a1
move.l #(1088*11)+2,d0
SYS FreeMem
.SkipVerify:
.End: movem.l (sp)+,d0-d1/a0-a1/a6
rts
;******************************* Task code ******************************
TaskCode:
PUTDEBUG <'Task: Entered'>
move.l 4,a6
;Grab the arguments passed down from our parent
move.l 4(sp),A_DEVICE ;Device pointer
;General initialization
lea StartSigs(A_DEVICE),a2
moveq #NumSigs-1,d2 ;Number of signals to allocate-1
.SigLoop:
moveq #-1,d0 ;-1 is any signal at all
SYS AllocSignal ;Allocate signals for I/O interrupts
moveq #0,d1 ;Convert bit number signal mask
bset d0,d1
move.l d1,(a2)+ ;Save in unit structure
dbra d2,.SigLoop
moveq #-1,d0 ;-1 is any signal at all
SYS AllocSignal ;Allocate a signal
move.b d0,TaskPort+MP_SIGBIT(A_DEVICE)
move.b #PA_SIGNAL,TaskPort+MP_FLAGS(A_DEVICE) ;Make message port "live"
moveq #-1,d0 ;-1 is any signal at all
SYS AllocSignal ;Allocate a signal
move.b d0,TimerPort+MP_SIGBIT(A_DEVICE)
move.l ThisTask(a6),TimerPort+MP_SIGTASK(A_DEVICE)
moveq #-1,d0 ;-1 is any signal at all
SYS AllocSignal ;Allocate a signal
move.b d0,DiskResourcePort+MP_SIGBIT(A_DEVICE)
move.l ThisTask(a6),DiskResourcePort+MP_SIGTASK(A_DEVICE)
bsr GetDrive
bsr Inquire
;Disk version only:
;Open trackdisk.device for all valid units and install diskchange handler.
IFEQ TYPE-DISK
moveq #MD_NUMUNITS-1,d2
move.b InquireBits(A_DEVICE),d3
lea Unit0(A_DEVICE),A_UNIT
.TDLoop:
moveq #-1,d0 ;-1 is any signal at all
SYS AllocSignal ;Allocate a signal
move.b d0,TDPort+MP_SIGBIT(A_UNIT)
move.l ThisTask(a6),TDPort+MP_SIGTASK(A_UNIT)
moveq #0,d0
move.b UnitNum(A_UNIT),d0
btst d0,d3
beq .NextTD
moveq #0,d1
lea TDName(pc),a0
lea TDIORequest(A_DEVICE),a1
SYS OpenDevice
tst.l d0
beq .TDOK
;Should never get here
.. move.w #$0f00,$dff180
bra ..
.TDOK:
;Install a diskchange handler via TD_ADDCHANGEINT.
lea TDIORequest(A_DEVICE),a1
lea ChangeInt(A_UNIT),a0
lea ChangeIntCode(pc),a2
move.l a2,IS_CODE(a0)
move.l A_UNIT,IS_DATA(a0)
move.l a0,IO_DATA(a1)
move.l #IS_SIZE,IO_LENGTH(a1) ;this is dumb
move.w #TD_ADDCHANGEINT,IO_COMMAND(a1)
SYS SendIO
.NextTD: lea MyUnit_Sizeof(A_UNIT),A_UNIT
dbra d2,.TDLoop
ENDC
IFNE INFO_LEVEL
push d0
moveq #0,d0
move.b InquireBits(A_DEVICE),d0
move.l d0,-(sp)
PUTDEBUG <'InquireBits=%lx'>
addq.l #4,sp
pop d0
ENDC
bsr SeekZeroAll ;Send all drives to track zero.
bsr FreeDrive
bra .NextMessage
;Main loop: Wait for a new message and handle disk changes.
.MainLoop:
; PUTDEBUG <'Task: Waiting'>
moveq #0,d0
move.b MP_SIGBIT+TaskPort(A_DEVICE),d1
bset d1,d0
move.l #500000,d1
bsr TimeOutWait
tst.l d0
bne .NextMessage
;0.5 seconds have passed without receiving any work, so we check for disk
;changes, then drop into .NextMessage.
;Note: A6 undefined.
.CheckDiskChange:
moveq #MD_NUMUNITS-1,d2
move.b InquireBits(A_DEVICE),d3
lea Unit0(A_DEVICE),A_UNIT
bsr GetDrive
move.l TimerIORequest+IO_DEVICE(A_DEVICE),a6
;Algorithm (for each unit):
;Check hardware diskchange bit. If disk was ejected, seek to track zero,
;clear DiskInDrive bit. End.
;If no disk in drive, check NoClick bit. If set, immediately step toward
;track zero (using custom step routine). If clear, check the
;Drive_LastChecked time against the current time. If less than 2.5 seconds,
;End. If >2.5 seconds, step the head toward track zero, unless already on
;track zero. Check the hardware diskchange bit. If a disk is present in the
;drive set the DiskInDrive bit and check the hardware write protect status,
;setting WriteProtected as needed. End.
;Note: We also increment the diskchange counter in the public part of the
;unit structure, if a disk was inserted or removed.
.UnitLoop:
move.b UnitNum(A_UNIT),d4
btst d4,d3
beq .NoCheck
lea TempTimeVal(A_DEVICE),a0
bsr GetSysTime
btst #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
beq .ThinkNoDisk
bsr SelectDriveSameMotor
btst #CIAB_DSKCHANGE,ciaapra
bne .NoChange
;Disk was removed.
bclr #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
bsr SeekZero
bsr MotorOff
bsr Clear
bra .Change
.ThinkNoDisk:
btst #TDPB_NOCLICK,TDU_PUBFLAGS(A_UNIT)
beq .Click
bsr SelectDriveSameMotor
bset #CIAB_DSKDIREC,ciabprb ;set to "out" (lower tracks)
bset #CIAB_DSKSTEP,ciabprb
bclr #CIAB_DSKSTEP,ciabprb ;step head
bset #CIAB_DSKSTEP,ciabprb
bsr Deselect
move.l TDU_SETTLEDELAY(A_UNIT),d0
bsr delay
bsr SelectDriveSameMotor
bra .ChkChg
.Click:
lea Drive_LastCheck(A_UNIT),a1
SYS SubTime
cmp.l #2,TV_SECS(a0)
blo .NoCheck
bhi .SkipMicro
cmp.l #500000,TV_MICRO(a0)
blo .NoCheck
.SkipMicro:
lea Drive_LastCheck(A_UNIT),a0
bsr GetSysTime
move.b UnitNum(A_UNIT),d2 ;for Seek
move.w TDU_CURRTRK(A_UNIT),d3
subq.w #2,d3
bpl .NotZero
moveq #2,d3
.NotZero: bsr SelectDriveSameMotor
bsr Seek
.ChkChg: btst #CIAB_DSKCHANGE,ciaapra
beq .NoChange ;no disk in drive
bset #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
bclr #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
btst #CIAB_DSKPROT,ciaapra
bne .Change ;not write protected
bset #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
.Change:
bsr Deselect
addq.l #1,TDU_COUNTER(A_UNIT)
;This code notifies users of TD_REMOVE and TD_ADDCHANGEINT via Cause.
push a6
move.l 4,a6
SYS Forbid
move.l TDRemoveInt(A_UNIT),d0
beq .NoRemoveInt
move.l d0,a1
SYS Cause
.NoRemoveInt:
move.l ChangeIntList+LH_HEAD(A_UNIT),a2
.IntLoop: move.l (a2),d0
beq .DoneInt
move.l IO_DATA(a2),a1
move.l d0,a2
SYS Cause
bra .IntLoop
.DoneInt: SYS Permit
pop a6
.NoChange:
lea LastCheck(A_DEVICE),a0
bsr GetSysTime
.NoCheck:
lea MyUnit_Sizeof(A_UNIT),A_UNIT
dbra d2,.UnitLoop
move.l 4,a6
bsr FreeDrive
.NextMessage:
btst #DEVB_Stopped,DEV_FLAGS(A_DEVICE) ;See if we are stopped
bne .MainLoop ;Device is stopped, ignore messages
lea TaskPort(A_DEVICE),a0
SYS GetMsg ;Get the next request
tst.l d0
beq .MainLoop ;no message?
move.l d0,A_IO ;Do this request
move.l IO_UNIT(A_IO),A_UNIT
;Handle TDB_EXTCOM and dispatch command
move.w IO_COMMAND(A_IO),d0
IFNE INFO_LEVEL
swap d0
clr.w d0
swap d0
move.l d0,-(sp)
PUTDEBUG <'Command=%ld'>
addq.l #4,sp
ENDC
bclr #TDB_EXTCOM,d0
beq .NoExt
move.l TDU_COUNTER(A_UNIT),d1
cmp.l IOTD_COUNT(A_IO),d1
bls .NoExt
.ChgErr: PUTDEBUG <'Change error!'>
move.b #TDERR_DiskChanged,IO_ERROR(A_IO)
bra .Reply
.NoExt: move.l #%000000111000111000011100,d1 ;specifies which commands should check for disk
btst d0,d1
beq .NoDiskNeeded
btst #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
beq .ChgErr
.NoDiskNeeded:
bsr GetDrive
lea CmdTable(pc),a0
add.w d0,d0
add.w (a0,d0.w),a0
moveq #0,d7 ;clear error flag
jsr (a0)
bsr FreeDrive
cmp.w #TD_ADDCHANGEINT,IO_COMMAND(A_IO)
beq .SkipReply
.Reply: move.l A_IO,a1
SYS ReplyMsg
.SkipReply:
;If at least 0.5 seconds has passed since last checking diskchange, do so
;now.
move.l TimerIORequest+IO_DEVICE(A_DEVICE),a6
lea TempTimeVal(A_DEVICE),a0
bsr GetSysTime
lea LastCheck(A_DEVICE),a1
SYS SubTime
tst.l TV_SECS(a0)
bne .CheckDiskChange
cmp.l #500000,TV_MICRO(a0)
bhs .CheckDiskChange
lea LastCheck(A_DEVICE),a0
bsr GetSysTime
move.l 4,a6
bra .NextMessage
GetSysTime:
cmp.w #36,LIB_VERSION(a6)
bls .OldKS
jmp _LVOGetSysTime(a6)
.OldKS:
;Compatibility routine for 1.2/1.3
movem.l d0-d1/a0-a2/a6,-(sp)
move.l a0,a2
lea TimerIORequest(A_DEVICE),a1
move.l 4,a6
move.w #TR_GETSYSTIME,IO_COMMAND(a1)
SYS DoIO
lea TimerIORequest(A_DEVICE),a1
move.l IOTV_TIME+TV_SECS(a1),TV_SECS(a2)
move.l IOTV_TIME+TV_MICRO(a1),TV_MICRO(a2)
movem.l (sp)+,d0-d1/a0-a2/a6
rts
IFEQ TYPE-DISK
ChangeIntCode:
rts
ENDC
;******************** External device routines ***********************
Open:
;A6 - device ptr
;A1 - IORequest
;D0 - unit number
;D1 - flags (not used)
PUTDEBUG <'Open: Entered'>
moveq #MD_NUMUNITS,d1
cmp.l d1,d0
bhs .Error
btst d0,InquireBits(a6)
beq .Error
lea Unit0(a6),a0
mulu.w #MyUnit_Sizeof,d0
add.l d0,a0
move.l a0,IO_UNIT(a1)
addq.w #1,LIB_OPENCNT(a6)
clr.b IO_ERROR(a1) ;no error
move.b #NT_REPLYMSG,LN_TYPE(a1) ;Mark IORequest as "complete"
PUTDEBUG <'Open: Done'>
rts
.Error:
PUTDEBUG <'Open: Failed'>
;VirusX didn't like this.
; move.b #IOERR_OPENFAIL,IO_ERROR(a1)
move.b #TDERR_BadUnitNum,IO_ERROR(a1)
rts
_Close:
;A6 - device ptr
;A1 - IORequest
;Must return either 0 or, if the device wishes to be unloaded, the segment
;list.
PUTDEBUG <'Close: Called'>
IFEQ TYPE-ROM
moveq #0,d0
rts
ENDC
IFEQ TYPE-DISK
moveq #0,d0
subq.w #1,LIB_OPENCNT(a6) ;Mark us as having one fewer openers
bne .End ;See if there is anyone left with us open
btst #LIBB_DELEXP,LIB_FLAGS(a6) ;See if we have a delayed expunge pending
beq .End
bsr Expunge ;Expunge will return with SegList in D0
.End: rts
ENDC
Expunge:
;A6 - device ptr
;Must return either 0 or SegList ptr in D0.
IFEQ TYPE-ROM
moveq #0,d0
rts
ENDC
IFEQ TYPE-DISK
movem.l d2/a2/a6,-(sp)
tst.w LIB_OPENCNT(a6) ;See if anyone has us open
bne .Delay
bsr CloseEverything ;clean up
move.l SegList(a6),d2 ;Store our seglist in d2
move.l a6,a1 ;Unlink from device list
move.l a6,a2 ;save
move.l 4,a6
SYS Remove ;Remove first (before FreeMem)
move.l a2,a1 ;device ptr
moveq #0,d0
move.w LIB_NEGSIZE(a2),d0
sub.l d0,a1 ;Calculate base of functions
add.w LIB_POSSIZE(a2),d0 ;Calculate size of functions + data area
SYS FreeMem
move.l d2,d0 ;Set up our return value
.End: movem.l (sp)+,d2/a2/a6
rts
.Delay: bset #LIBB_DELEXP,LIB_FLAGS(a6) ;Set the delayed expunge flag
moveq #0,d0
bra .End
ENDC
BeginIO:
;A1 - IORequest
;A6 - device ptr
movem.l d7/a3-a6,-(sp)
move.l a1,A_IO
move.l IO_UNIT(A_IO),A_UNIT
move.l a6,A_DEVICE
move.l 4,a6
clr.b IO_ERROR(A_IO)
move.b #NT_MESSAGE,LN_TYPE(A_IO) ;So WaitIO() is guaranteed to work
;Decide whether the command is immediate or queued
move.w IO_COMMAND(A_IO),d1
bclr #15,d1
cmp.w #HighestCommand,d1
bhi .BadCmd
move.l #%111011001111000111000011,d0 ;specifies what commands are immediate
btst d1,d0
beq .Queue
add.w d1,d1
lea CmdTable(pc),a0
add.w (a0,d1.w),a0
moveq #0,d7 ;clear error flag
jsr (a0)
.Reply: btst #IOB_QUICK,IO_FLAGS(A_IO)
bne .End
move.l A_IO,a1
SYS ReplyMsg
.End: movem.l (sp)+,d7/a3-a6
rts
.Queue: bclr #IOB_QUICK,IO_FLAGS(A_IO) ;We did NOT complete this quickly
lea TaskPort(A_DEVICE),a0
move.l A_IO,a1
SYS PutMsg
bra .End
.BadCmd: move.b #IOERR_NOCMD,IO_ERROR(a1)
bra .Reply
AbortIO:
;A6 - device ptr
;A1 - IORequest
move.b #IOERR_ABORTED,IO_ERROR(a1) ;We always say we succeed(ed)
moveq #0,d0 ;another success code
rts
;**************** Device command (IO_COMMAND) routines *************************
;Note: A6 = ExecBase upon entering a command routine.
;The low-level routines load $DFF000 into A6 when needed.
SaveRegs equrl d0-d3/d6-d7/a0-a2/a6
Read:
PUTDEBUG <'Read: Called'>
movem.l SaveRegs,-(sp)
bsr RWSetup
tst.l d7
bne FinishRW
bsr DISK_Update ;required because of complex write scheme
;Check for sector label nonsense
btst #7,IO_COMMAND(A_IO)
beq .NoLabel
move.l IOTD_SECLABEL(A_IO),d2
bne ReadSecLabel
.NoLabel:
bsr DISK_Read
PUTDEBUG <'Read: Done'>
bra FinishRW
Format:
Write: movem.l SaveRegs,-(sp)
bsr RWSetup
tst.l d7
bne FinishRW
;Check for sector label nonsense
btst #7,IO_COMMAND(A_IO)
beq .NoLabel
move.l IOTD_SECLABEL(A_IO),d2
bne WriteSecLabel
.NoLabel:
bsr DISK_Write
FinishRW: clr.l IO_ACTUAL(A_IO)
move.b d7,IO_ERROR(A_IO)
bne .Skip
move.l IO_LENGTH(A_IO),IO_ACTUAL(A_IO)
.Skip: movem.l (sp)+,SaveRegs
rts
RWSetup:
move.l #512*11,d6
move.l #_custom,a6
move.l IO_LENGTH(A_IO),d0
move.l IO_OFFSET(A_IO),d1
move.l IO_DATA(A_IO),a0
move.l d1,d2 ;offset
add.l d0,d2 ;length
cmp.l #901120,d2
bhi .Error
bsr SelectDrive
bra SelectSide
.Error: moveq #DISK_BadParameter,d7
rts
;Here are the routines for dealing with read/write requests that involve
;the sector label. Note that, unlike the usual read/write routines of my
;trackdisk, the normal parameter restrictions (e.g. IO_OFFSET and IO_LENGTH
;must be a multiple of 512) MUST be observed. And I don't check for illegal
;parameters, either. These routines are optimized for compactness rather
;than performance because they are rarely (if ever) used.
;Enter with:
;IO_LENGTH: D0
;IO_OFFSET: D1
;IO_DATA: A0
ReadSecLabel:
PUTDEBUG <'READSECLABEL!'>
move.l d2,a1
move.l d0,d2
move.l #512,d0
.Read: bsr DISK_Read
move.l d1,d3
add.l d0,d1
add.l d0,a0
divu.w d6,d3 ;offset/tracksize
swap d3 ;remainder=sector# (in bytes)
lsr.w #5,d3 ;divide by 32 to get label offset
lea SectorLabels(A_DEVICE),a2
lea (a2,d3.w),a2 ;get pointer to label
moveq #3,d3
.. move.l (a2)+,(a1)+ ;copy label
dbra d3,..
sub.l d0,d2
bne .Read
bra FinishRW
WriteSecLabel:
PUTDEBUG <'WRITESECLABEL!'>
move.l d2,a1
move.l d0,d2
.Write:
move.l d1,d3
divu.w d6,d3 ;offset/tracksize
swap d3 ;remainder=sector# (in bytes)
lsr.w #5,d3 ;divide by 32 to get label offset
;We check to see whether we can write a full track. (Somehow, I can't
;ignore performance completely :-)).
tst.w d3
bne .Read
cmp.l d6,d0
bhs .FullTrack
.Read: moveq #0,d0
bsr DISK_Read ;force a track read
lea SectorLabels(A_DEVICE),a2
lea (a2,d3.w),a2 ;get pointer to label
moveq #3,d3
.. move.l (a1)+,(a2)+ ;copy label
dbra d3,..
move.l #512,d0
.L1: bsr DISK_Write
add.l d0,d1
add.l d0,a0
sub.l d0,d2
bne .Write
bra FinishRW
.FullTrack:
lea SectorLabels(A_DEVICE),a2
moveq #((16*11)/4)-1,d3
.. move.l (a1)+,(a2)+ ;copy all sector labels
dbra d3,..
move.l d6,d0
bra .L1
Update: move.l d7,-(sp)
moveq #0,d7
bsr DISK_Update
move.b d7,IO_ERROR(A_IO)
move.l (sp)+,d7
rts
Clear:
;This routine marks the track buffer as invalid.
st BufferDrive(A_DEVICE)
bclr #DEVB_Dirty,DEV_FLAGS(A_DEVICE)
rts
Motor:
;Controls the drive motor
;If IO_LENGTH=1, motor on
; IO_LENGTH=0, motor off
;Old motor state (0=off, anything else means on) stored in IO_ACTUAL.
push d2
clr.l IO_ACTUAL(A_IO)
move.b UnitNum(A_UNIT),d2
btst d2,MotorState(A_DEVICE)
beq .WasOff
move.l #1,IO_ACTUAL(A_IO)
.WasOff: tst.l IO_LENGTH(A_IO)
beq .Off
;Turn motor on
bsr SelectDrive
bra .End
.Off:
;Turn motor off
bclr d2,MotorState(A_DEVICE)
bsr Deselect
bset #CIAB_DSKMOTOR,ciabprb ;motor off
addq.b #3,d2
bclr d2,ciabprb ;select drive X
.End: pop d2
rts
ChangeState:
;Returns whether there is a disk currently in the drive
;IO_ACTUAL=0 if disk in drive
;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
clr.l IO_ACTUAL(A_IO)
btst #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
bne .End
move.l #1,IO_ACTUAL(A_IO) ;no disk in drive
.End: rts
ChangeNum:
;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
move.l TDU_COUNTER(A_UNIT),IO_ACTUAL(A_IO)
rts
ProtStatus:
;IO_ACTUAL=0 if disk is not write protected
;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
;Very important for compatibility: we must return an error (disk changed)
;if there's no disk in the drive.
btst #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
bne .OK
move.b #TDERR_DiskChanged,IO_ERROR(A_IO)
rts
.OK: clr.l IO_ACTUAL(A_IO)
btst #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
beq .End ;not write protected
move.l #1,IO_ACTUAL(A_IO) ;no disk in drive
.End: rts
GetDriveType:
;Returns type of drive in IO_ACTUAL
;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
move.l #DRIVE3_5,IO_ACTUAL(A_IO)
rts
GetNumTracks:
;Returns number of tracks (note: not cylinders) in IO_ACTUAL
;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
move.l #160,IO_ACTUAL(A_IO)
rts
GetGeometry:
;Fills in DriveGeometry structure pointed to by IO_DATA
push a0
move.l IO_DATA(A_IO),a0
move.l #512,dg_SectorSize(a0) ;in bytes
move.l #1760,dg_TotalSectors(a0) ;total # of sectors on drive
move.l #80,dg_Cylinders(a0) ;number of cylinders
move.l #22,dg_CylSectors(a0) ;number of sectors/cylinder
move.l #2,dg_Heads(a0) ;number of surfaces
move.l #11,dg_TrackSectors(a0) ;number of sectors/track
clr.l dg_BufMemType(a0) ;preferred buffer memory type
clr.b dg_DeviceType(a0) ;codes as defined in the SCSI-2 spec
move.b #DGF_REMOVABLE,dg_Flags(a0) ;flags, including removable
clr.w dg_Reserved(a0)
pop a0
rts
MyStop: bset #DEVB_Stopped,DEV_FLAGS(A_DEVICE)
rts
Start: movem.l d0-d1/a0-a1,-(sp)
bset #DEVB_Stopped,DEV_FLAGS(A_DEVICE)
moveq #0,d0
move.b MP_SIGBIT+TaskPort(A_DEVICE),d1
bset d1,d0
lea Task(A_DEVICE),a1
SYS Signal
movem.l (sp)+,d0-d1/a0-a1
rts
;Note: This seek routine can't really be depended on by user programs,
;because the disk.resource will corrupt the side select bit.
TDSeek: push d3
bsr SelectDriveSameMotor
move.l IO_OFFSET(A_IO),d3
divu.w #512*11,d3
cmp.w #159,d3
bhi .Error
bsr Seek
.End: pop d3
rts
.Error: move.b #TDERR_NotSpecified,IO_ERROR(A_IO)
bra .End
TDRawRead:
movem.l d0-d1/d3/a6,-(sp)
move.l #_custom,a6
moveq #0,d1
bsr DoRaw
movem.l (sp)+,d0-d1/d3/a6
rts
RawWrite:
movem.l d0-d1/d3/a6,-(sp)
move.l #_custom,a6
btst #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
bne .WPErr
move.w #1<<14,d1
bsr DoRaw
move.l #3*1000,d0
bsr delay ;post-write delay
.End: movem.l (sp)+,d0-d1/d3/a6
rts
.WPErr: move.b #TDERR_WriteProt,IO_ERROR(A_IO)
bra .End
DoRaw:
;Enter with bit 14 set in d1 according to read/write (for dsklen)
;Uses d0-d1/d3 (not saved)
;Assumes $dff000 in A6
clr.l IO_ACTUAL(A_IO)
bsr SelectDrive
move.l IO_OFFSET(A_IO),d3
cmp.l #159,d3
bhi .Err
bsr Seek
tst.l d7
bne .Err
move.l IO_DATA(A_IO),dskpt(a6)
move.w #ADKF_WORDSYNC,adkcon(a6)
btst #IOTDB_WORDSYNC,IO_FLAGS(A_IO)
beq .SkipSync
move.w #ADKF_SETCLR+ADKF_WORDSYNC,adkcon(a6)
.SkipSync:
move.w #ADKF_MSBSYNC,adkcon(a6)
move.w #ADKF_SETCLR+ADKF_FAST+ADKF_MFMPREC,adkcon(a6)
bsr PreComp
move.w #DMAF_SETCLR+DMAF_MASTER+DMAF_DISK,dmacon(a6) ;enable disk DMA
move.w #$4489,dsksync(a6) ;set magic sync word
bsr ClearSigs
move.w #INTF_SETCLR+INTF_DSKBLK,intena(a6) ;enable int
move.l IO_LENGTH(A_IO),d0
cmp.l #32766,d0
bhi .Error
lsr.l #1,d0
bset #15,d0
or.w d1,d0
btst #IOTDB_INDEXSYNC,IO_FLAGS(A_IO)
beq .NoIndex
move.w d0,IndexDskLen(A_DEVICE)
bsr EnableIndex
bra .Wait
.NoIndex:
move.w d0,dsklen(a6)
move.w d0,dsklen(a6)
.Wait: move.l BlockSig(A_DEVICE),d0
move.l #900*1000,d1
bsr TimeOutWait
beq .Error
move.w #0,dsklen(a6)
move.w #INTF_DSKBLK,intena(a6) ;disable int
bsr DisableIndex
bsr ClearSigs
.End: tst.b IO_ERROR(A_IO)
bne .EndRts
move.l IO_LENGTH(A_IO),IO_ACTUAL(A_IO)
.EndRts: rts
.Error: bsr StopDMA
.Err: move.b #TDERR_NotSpecified,IO_ERROR(A_IO)
bra .End
AddChangeInt:
;This command uses the linkage fields of the IORequest to add a ChangeInt
;request to a list maintained in the unit structure. This must NOT be
;ReplyMsg'ed to avoid destroying the linkage fields - a special compare in
;the task code handles this.
movem.l d0-d1/a0-a1,-(sp)
move.l A_IO,a1
lea ChangeIntList(A_UNIT),a0
SYS AddHead ;could use any list add routine
movem.l (sp)+,d0-d1/a0-a1
rts
RemChangeInt:
;This command unlinks the AddChangeInt request from the task's port.
;This is (and must be) an immediate command.
;Note that because this command occurs asyncronously to the task code, the
;task code that scans the ChangeInt list must be protected with a Forbid.
movem.l d0-d1/a0-a1,-(sp)
SYS Forbid
move.l A_IO,a1
SYS Remove
SYS Permit
movem.l (sp)+,d0-d1/a0-a1
rts
TDRemove:
;This command is obsolete, but is unfortuntely used by the ROM filesystem.
;It accepts an Interrupt structure (for Cause) in IO_DATA. Only one user per
;unit is permitted.
tst.l TDRemoveInt(A_UNIT)
beq .Ok
move.b #TDERR_DriveInUse,IO_ERROR(A_IO)
rts
.Ok: move.l IO_DATA(A_IO),TDRemoveInt(A_UNIT)
rts
GetDrive:
;Obtain the use of this unit via disk.resource.
; PUTDEBUG <'GetDrive: Called'>
movem.l d0-d1/a0-a2/a6,-(sp)
lea DiskResourceUnit(A_DEVICE),a2
.GetUnit:
move.l DiskResourceBase(A_DEVICE),a6
move.l a2,a1
jsr DR_GETUNIT(a6)
tst.l d0
bne .End
lea DiskResourcePort(A_DEVICE),a0
move.l 4,a6
SYS WaitPort
bra .GetUnit
.End:
IFEQ TYPE-DISK
move.l TDUnit(A_UNIT),d0
beq .EndEnd
move.l d0,a0
move.w TDU_CURRTRK(a0),TDU_CURRTRK(A_UNIT)
ENDC
.EndEnd: movem.l (sp)+,d0-d1/a0-a2/a6
; PUTDEBUG <'GetDrive: Done'>
rts
FreeDrive:
;Release this unit to other users of disk.resource.
movem.l d0-d1/a0-a1/a6,-(sp)
IFEQ TYPE-DISK
move.l TDUnit(A_UNIT),d0
beq .Skip
move.l d0,a0
move.w TDU_CURRTRK(A_UNIT),TDU_CURRTRK(a0)
.Skip:
ENDC
move.l DiskResourceBase(A_DEVICE),a6
jsr DR_GIVEUNIT(a6)
movem.l (sp)+,d0-d1/a0-a1/a6
rts
;***************************** Low-level disk code **************************
;Standard register usage:
;A6 - $dff000
;A5 (A_DEVICE) - device ptr
;A4 (A_UNIT) - unit ptr
;A3 (A_IO) - pointer to IO request
;For reading/writing:
;D0.L - length (bytes)
;D1.L - offset (bytes)
;A0.L - buffer
;NOTE: Error code returned in D7.L - 0 if ok, else error
;General errors
DISK_BadParameter equ TDERR_NotSpecified
;Read errors
DISK_NoSync equ TDERR_NoSecHdr
DISK_BadHeader equ TDERR_BadHdrSum
DISK_BadData equ TDERR_BadSecSum
;Write errors
DISK_WriteProtected equ TDERR_WriteProt
DISK_VerifyError equ TDERR_NotSpecified
IFND dskpt
dskpt equ dskpth
ENDC
;Note: This is a public value, but it is NOT meant to be changed!!!
DISK_RawBufSize equ 13630
ciabprb equ $bfd100
ciaapra equ $bfe001
ciabddrb equ $bfd300
ciaaddra equ $bfe201
***************************************************************
;Important disk parameters, summary:
;Step rate: 3ms. 4ms when looking for track zero.
;Settle time: 15ms
;Post-write delay: 3ms (officially 1.3ms)
;Side select delay: 2ms (officially 1.3ms)
***************************************************************
DISK_Read:
movem.l d0-d5/a0/a1,-(sp)
move.l a0,a1 ;destination ptr in A1
move.l d0,d5
;The meat of the read routine...
.ReadLoop:
move.l d1,d3
divu.w d6,d3 ;d3.w is track #
move.l d3,d4
clr.w d4
swap d4 ;d4.l is byte offset into track
bsr MinSeek
tst.l d4 ;any offset?
bne .Complex ;yes
cmp.l d6,d5 ;check remaining length
blo .Complex ;if not a track, forget it
move.l a1,d0
btst #0,d0 ;word aligned?
bne .Complex ;no, do it the long way
move.l a1,a0
bsr ReadTrackAndDecodeNoBuffer
tst.l d7
bne .End
sub.l d6,d5 ;update length
beq .End
add.l d6,a1 ;update destination pointer
add.l d6,d1 ;update offset
bra .ReadLoop
.Complex:
move.l DecodedBuffer(A_DEVICE),a0
bsr ReadTrackAndDecodeBuffered
tst.l d7
bne .End
add.l d4,a0
sub.l d6,d4
neg.l d4
add.l d4,d1 ;update offset
;D4.L - number of bytes that could be transferred from this track
;D5.L - number of bytes left in the entire Read request
cmp.l d5,d4
bhs .FinishUp
sub.l d4,d5 ;update length
move.l d4,d0
bsr CopyMem
add.l d0,a1 ;update destination pointer
bra .ReadLoop
.FinishUp:
move.l d5,d0
bsr CopyMem ;use number of bytes left in entire request
.End: movem.l (sp)+,d0-d5/a0/a1
rts
SelectDrive:
;Selects drive AND turns on motor
;Selects UnitNum(A_UNIT).
movem.l d0/d2,-(sp)
move.b UnitNum(A_UNIT),d2
bsr Deselect
bclr #CIAB_DSKMOTOR,ciabprb ;motor on
move.l d2,d0
addq.b #3,d0
bclr d0,ciabprb ;select drive X
bset d2,MotorState(A_DEVICE)
moveq #4,d2
.Wait: btst #CIAB_DSKRDY,ciaapra
beq .End
move.l #100*1000,d0
bsr delay
dbra d2,.Wait
.End: movem.l (sp)+,d0/d2
rts
Deselect: or.b #CIAF_DSKSEL0+CIAF_DSKSEL1+CIAF_DSKSEL2+CIAF_DSKSEL3,ciabprb ;Deselect all drives
rts
SelectDriveSameMotor:
;Select a drive WITHOUT changing the current motor state
;Selects UnitNum(A_UNIT).
push d2
move.b UnitNum(A_UNIT),d2
bsr Deselect
bclr #CIAB_DSKMOTOR,ciabprb ;motor on
btst d2,MotorState(A_DEVICE)
bne .WasOn
bset #CIAB_DSKMOTOR,ciabprb ;motor off
.WasOn: addq.b #3,d2
bclr d2,ciabprb ;select drive X
pop d2
_RTS: rts
DISK_Update:
;Flush track buffer if "dirty"
bclr #DEVB_Dirty,DEV_FLAGS(A_DEVICE)
beq _RTS
movem.l d0-d2/a0/a2/a6,-(sp)
move.l #_custom,a6
;This is somewhat tricky, because the currently selected drive may not be
;the drive that we want to write to!
move.b UnitNum(A_UNIT),d0
move.w TDU_CURRTRK(A_UNIT),d1
move.b BufferDrive(A_DEVICE),UnitNum(A_UNIT) ;fake drive
move.b BufferTrack(A_DEVICE),TDU_CURRTRK+1(A_UNIT) ;fake track
bsr SelectDrive
bsr SelectSide
;The logic of this routine is complicated by the need to support the write
;optimization scheme (see DISK_Write for details). What we need to do is
;check the WriteMap to determine whether a ReadTrackAndDecode is required
;before finally writing...
move.l WriteMap(A_DEVICE),d2
and.l #%11111111111,d2 ;changes for different drive types!
cmp.l #%11111111111,d2 ;changes for different drive types!
beq .SkipRead
move.l DecodedBuffer(A_DEVICE),a0
bsr ReadTrackAndDecode
tst.l d7
bne .End
.SkipRead:
move.l d7,WriteMap(A_DEVICE)
move.l DecodedBuffer(A_DEVICE),a2
bsr EncodeAndWriteTrack
move.b d0,UnitNum(A_UNIT)
move.w d1,TDU_CURRTRK(A_UNIT)
bsr SelectDrive
.End: movem.l (sp)+,d0-d2/a0/a2/a6
rts
SelectSide:
;Select the correct side based on current track. This routine MUST be
;called by certain routines (e.g. Update, Write) because the side select is
;lost after a GiveUnit!
bclr #CIAB_DSKSIDE,ciabprb ;set to upper
btst #0,TDU_CURRTRK+1(A_UNIT)
bne .skip
bset #CIAB_DSKSIDE,ciabprb ;set to lower
.skip: push d0
move.l #2*1000,d0
bsr delay
pop d0
rts
;Enter with destination track in d3.b
;Note: This routine checks for valid track numbers -- if either the
;current track number or the destination is invalid, PANIC.
MinSeek:
;Only call MinSeek if it is certain that the side select has not been
;changed!
cmp.b TDU_CURRTRK+1(A_UNIT),d3
bne Seek
move.w d0,-(sp)
move.b BufferDrive(A_DEVICE),d0
cmp.b UnitNum(A_UNIT),d0
bne .Seek
move.w (sp)+,d0
rts
.Seek: move.w (sp)+,d0
Seek: movem.l d0/d3-d4,-(sp)
bsr DISK_Update
tst.l d7
bne .End
move.l TDU_STEPDELAY(A_UNIT),d0
cmp.b #160,d3
bhs .Error
;Set head
bclr #CIAB_DSKSIDE,ciabprb ;set to upper
btst #0,d3
bne .skip
bset #CIAB_DSKSIDE,ciabprb ;set to lower
.skip:
move.w TDU_CURRTRK(A_UNIT),d4
lsr.b #1,d4
cmp.b #80,d4
bhs .Error
move.b d3,TDU_CURRTRK+1(A_UNIT)
lsr.b #1,d3
bset #CIAB_DSKDIREC,ciabprb ;set to "out" (lower tracks)
sub.b d3,d4
beq .SideSelectOnly
bhi .StepIn ;Go if DISK_CurrentTrack > Destination
bclr #CIAB_DSKDIREC,ciabprb ;set to "in" (higher tracks)
neg.b d4
.StepIn:
bsr SelectDriveSameMotor
bset #CIAB_DSKSTEP,ciabprb
bclr #CIAB_DSKSTEP,ciabprb ;step head
bset #CIAB_DSKSTEP,ciabprb
bsr Deselect
bsr delay
subq.b #1,d4
bne .StepIn
move.l TDU_SETTLEDELAY(A_UNIT),d0
bsr delay
bsr SelectDriveSameMotor
.End: movem.l (sp)+,d0/d3-d4
rts
.SideSelectOnly:
move.l #2*1000,d0
bsr delay
bra .End
.Error:
; move.w #$f00,$dff180
bra .Error
;*************************** Delay code ****************************
delay:
;Enter with microseconds in D0.L
movem.l d0-d1/a0-a1/a6,-(sp)
lea TimerIORequest(A_DEVICE),a1
move.w #TR_ADDREQUEST,IO_COMMAND(a1)
clr.l TV_SECS+IO_SIZE(a1)
move.l d0,TV_MICRO+IO_SIZE(a1)
move.l 4,a6
SYS DoIO
;Clear signal bit (TimeOutWait depends on the signal bit being 'correct').
lea TimerIORequest(A_DEVICE),a1
move.l MN_REPLYPORT(a1),a0
move.b MP_SIGBIT(a0),d0
moveq #0,d1
bset d0,d1
moveq #0,d0
SYS SetSignal
movem.l (sp)+,d0-d1/a0-a1/a6
rts
TimeOutWait:
;Wait for signal mask in D0, but include a time-out specified in D1 (usecs).
;Returns wait mask in D0, or 0 if a time-out occured (Z flag will be set).
movem.l d1-d4/a0-a2/a6,-(sp)
move.l d0,d3
move.l 4,a6
lea TimerIORequest(A_DEVICE),a1
move.l a1,a2
move.w #TR_ADDREQUEST,IO_COMMAND(a1)
clr.l TV_SECS+IO_SIZE(a1)
move.l d1,TV_MICRO+IO_SIZE(a1)
move.l MN_REPLYPORT(a1),a0
moveq #0,d2
move.b MP_SIGBIT(a0),d1
bset d1,d2
SYS SendIO
move.l d3,d0
add.l d2,d0 ;equivilent to OR in this case
SYS Wait
and.l d3,d0 ;exclude timer signal
move.l d0,d4
beq .TimeOut ;if zero, timer signal was the only signal
move.l a2,a1
SYS AbortIO
.TimeOut: move.l a2,a1
SYS WaitIO
moveq #0,d0 ;value
move.l d2,d1 ;mask (timer sig)
SYS SetSignal ;make SURE the timer signal is clear
move.l d4,d0
movem.l (sp)+,d1-d4/a0-a2/a6
rts
;*************************** End delay code ****************************
;*************************** Interrupt routines ************************
;------------------------------------------
;Register contents upon entering a handler:
;D1 - (INTENAR) AND (INTREQR)
;A0 - $dff000
;A1 - data ptr (device ptr in this case)
;A6 - ExecBase
;d0, d1, a0, a1, a5, and a6 are scratch.
;-----------------------------------------
;However, the disk.resource autodoc indicates the following arrangement for
;interrupts installed by GetUnit:
;D0/D1/A0/A1 are scratch
;A1 points to IS_DATA (device pointer in this case).
;Make no other assumptions!
SyncInt: addq.w #1,SyncCount(a1)
move.l SyncSig(a1),d0
lea Task(a1),a1
push a6
move.l 4,a6
SYS Signal
pop a6
;Delay approximately 63us to avoid two sync interrupts
;This could be improved.
moveq #1,d0
.L1: move.b vhposr+_custom,d1
.L2: cmp.b vhposr+_custom,d1
beq .L2
dbra d0,.L1
move.w #INTF_DSKSYNC,intreq+_custom ;clear sync
rts
BlockInt:
push a6
move.l #_custom,a6
move.w #INTF_DSKBLK,intreq(a6)
;Test DEVB_Verify flag. If set, initiate a read into the verify buffer.
bclr #DEVB_Verify,DEV_FLAGS(a1)
beq .NoVerify
move.w #INTF_SETCLR+INTF_DSKSYNC,intena(a6) ;enable sync int
move.l VerifyBuffer(a1),dskpt(a6)
move.w #ADKF_SETCLR+ADKF_WORDSYNC,adkcon(a6)
move.w #0,dsklen(a6)
move.w #$9761,dsklen(a6)
move.w #$9761,dsklen(a6)
move.w #$4489,dsksync(a6) ;set magic sync word
;Now the first sync interrupt we get will be known to be valid, because the
;DMA read is fully started.
bra .End
.NoVerify:
move.l BlockSig(a1),d0
lea Task(a1),a1
move.l 4,a6
SYS Signal
.End: pop a6
rts
IndexInt:
move.w IndexDskLen(a1),d0
beq .End
move.w d0,dsklen+_custom
move.w d0,dsklen+_custom
clr.w IndexDskLen(a1)
.End: rts
EnableIndex:
movem.l d0-d1/a0-a1/a6,-(sp)
move.l CIABase(A_DEVICE),a6
moveq #16,d0
SYS SetICR ;clear interrupt
move.l #16+128,d0
SYS AbleICR ;enable interrupt
movem.l (sp)+,d0-d1/a0-a1/a6
rts
DisableIndex:
movem.l d0-d1/a0-a1/a6,-(sp)
move.l CIABase(A_DEVICE),a6
moveq #16,d0
SYS AbleICR ;disable interrupt
moveq #16,d0
SYS SetICR ;clear interrupt
movem.l (sp)+,d0-d1/a0-a1/a6
rts
;*********************** End interrupt routines ************************
ClearSigs:
movem.l d0-d1/a0-a1/a6,-(sp)
move.w #INTF_DSKBLK+INTF_DSKSYNC,intreq+_custom ;clear sync+done
clr.w SyncCount(A_DEVICE)
clr.w IndexDskLen(A_DEVICE)
move.l 4,a6
moveq #0,d0 ;value
move.l SyncSig(A_DEVICE),d1
add.l BlockSig(A_DEVICE),d1
SYS SetSignal
movem.l (sp)+,d0-d1/a0-a1/a6
rts
SetRegs:
;This routine enables the block interrupt, but leaves sync interrupts
;disabled.
move.l RawBuffer(A_DEVICE),dskpt(a6)
move.w #ADKF_MSBSYNC+ADKF_PRECOMP0+ADKF_PRECOMP1,adkcon(a6)
move.w #ADKF_SETCLR+ADKF_FAST+ADKF_WORDSYNC+ADKF_MFMPREC,adkcon(a6)
move.w #DMAF_SETCLR+DMAF_MASTER+DMAF_DISK,dmacon(a6) ;enable disk DMA
bsr ClearSigs
move.w #INTF_SETCLR+INTF_DSKBLK,intena(a6) ;enable block int
rts
PreComp:
;Enter with $dff000 in A6
movem.l d0-d1,-(sp)
move.w #ADKF_PRECOMP0+ADKF_PRECOMP1,adkcon(a6) ;no precomp
move.w TDU_CURRTRK(A_UNIT),d0
cmp.w TDU_COMP01TRACK(A_UNIT),d0
bls .End
move.w #ADKF_SETCLR+ADKF_PRECOMP0,d1
cmp.w TDU_COMP10TRACK(A_UNIT),d0
bls .Done
move.w #ADKF_SETCLR+ADKF_PRECOMP1,d1
cmp.w TDU_COMP11TRACK(A_UNIT),d0
bls .Done
move.w #ADKF_SETCLR+ADKF_PRECOMP0+ADKF_PRECOMP1,d1
.Done: move.w d1,adkcon(a6)
.End: movem.l (sp)+,d0-d1
rts
ScanSync:
;Scan for sync mark
;A2 - pointer to raw data -- updated
;Error code in D7
bsr WaitWordSync
tst.l d7
bne .End
;This routine is trickier than it appears. The trick is that we must NOT
;assume a $4489 at the beginning of our buffer. This phenomenon occurs when
;the DMA starts in the middle of the first sync word. The second sync word
;is thrown away by the hardware. It sounds exotic, but it actually happens
;quite often!
push a4
move.l RawBuffer(A_DEVICE),a4
cmp.l a4,a2
add.w #DISK_RawBufSize,a4 ;does not affect flags
beq .found ;if start of buffer, don't scan for sync!!!
.Sync: cmpi.w #$4489,(a2)+
beq .found
cmp.l a2,a4
bhi .Sync
.Error: pop a4
moveq #DISK_NoSync,d7
rts
.found: cmpi.w #$4489,(a2)
bne .ok
addq.l #2,a2
cmp.l a2,a4
bhi .found
bra .Error
.ok: pop a4
.End: rts
StopDMA: move.w #0,dsklen(a6)
move.w #1<<15,dsklen(a6)
move.w #1<<15,dsklen(a6) ;zero-length DMA transfer
move.w #INTF_DSKBLK+INTF_DSKSYNC,intena(a6) ;disable ints
bra ClearSigs
WaitWordSync:
;Wait for a sync mark or disk block done
;Will return an error in 300ms if nothing happens.
movem.l d0-d1,-(sp)
.Wait: tst.w SyncCount(A_DEVICE)
bne .Sync
move.l SyncSig(A_DEVICE),d0
add.l BlockSig(A_DEVICE),d0
move.l #300*1000,d1 ;time-out (300ms)
bsr TimeOutWait
beq .Error
and.l BlockSig(A_DEVICE),d0
bne .Done
bra .Wait
.Sync: subq.w #1,SyncCount(A_DEVICE)
.End: movem.l (sp)+,d0-d1
rts
.Done: move.w #$C000,SyncCount(A_DEVICE) ;this is obscure - should change
bra .End
.Error: moveq #DISK_NoSync,d7
bra .End
ReadTrackAndDecodeBuffered:
push d0
move.l WriteMap(A_DEVICE),d0
beq .OK ;no optimized writes to worry about
and.l #%11111111111,d0 ;changes for different drive types!
cmp.l #%11111111111,d0 ;changes for different drive types!
beq .End
moveq #-1,d0
move.l d0,WriteMap(A_DEVICE) ;mark sectors as filled
bra .ReadTrack
.OK: move.b UnitNum(A_UNIT),d0
cmp.b BufferDrive(A_DEVICE),d0
bne .ReadTrack
move.w TDU_CURRTRK(A_UNIT),d0
cmp.b BufferTrack(A_DEVICE),d0
bne .ReadTrack
.End: pop d0
rts
.ReadTrack:
pop d0
ReadTrackAndDecode:
;Input: Buffer ptr in A0
;Returns error code in D7
move.b UnitNum(A_UNIT),BufferDrive(A_DEVICE)
move.b TDU_CURRTRK+1(A_UNIT),BufferTrack(A_DEVICE)
ReadTrackAndDecodeNoBuffer:
move.w d0,-(sp)
move.b TDU_RETRYCNT(A_UNIT),d0
.Retry: moveq #0,d7
bsr ReadTrackAndDecodeNoRetry
tst.l d7
beq .End
subq.b #1,d0
bpl .Retry
.End: move.w (sp)+,d0
rts
ReadTrackAndDecodeNoRetry:
movem.l d0-d6/a0-a2,-(sp)
;Initiate a raw track read
;Because we are using WORDSYNC interrupts, this code is VERY VERY tricky!!!!!
bsr SetRegs
move.w #0,dsksync(a6) ;set invalid sync word
;At this point sync interrupts will stop happenning, because $0000 will
;never occur. So, we clear the interrupt and enable it.
bsr ClearSigs
move.w #INTF_SETCLR+INTF_DSKSYNC,intena(a6) ;enable sync int
move.w #0,dsklen(a6)
move.w #$9A9E,dsklen(a6)
move.w #$9A9E,dsklen(a6) ;read approx. 13,628 bytes
move.w #$4489,dsksync(a6) ;set magic sync word
;Now the first sync interrupt we get will be known to be valid, because the
;DMA read is fully started.
;Decode track
move.l RawBuffer(A_DEVICE),a2
moveq #11-1,d3 ;# of sectors
move.l #$55555555,d2
;Wait for first sync marks...
bsr WaitWordSync
tst.l d7
bne .End
.SecLoop:
bsr ScanSync
tst.l d7
bne .End
bsr DecodeLong ;get header
subq.l #8,a2
and.w #$ff00,d0 ;mask off sector number
move.l d0,d1
cmp.w #$0a00,d1
bhi .HeaderError
add.w d1,d1 ;convert to sector offset
lsr.w #4,d0 ;get sector label offset
lea SectorLabels(A_DEVICE),a1
lea (a1,d0.w),a1
;This code supports the write optimization...
lsr.w #4,d0 ;sector number
move.l WriteMap(A_DEVICE),d4
btst d0,d4 ;should we avoid reading this sector?
bne .EndLoop ;yes, skip to next sector
move.l d1,-(sp)
moveq #0,d5
move.l (a2)+,d0
eor.l d0,d5
move.l (a2)+,d0
eor.l d0,d5
;Decode and checksum sector label
moveq #3,d6
.Label: move.l 16(a2),d4
eor.l d4,d5
and.l d2,d4
move.l (a2)+,d1
eor.l d1,d5
and.l d2,d1
add.l d1,d1
or.l d1,d4
move.l d4,(a1)+
dbra d6,.Label
and.l d2,d5
lea 16(a2),a2 ;point at header checksum
move.l (sp)+,d1
bsr DecodeLong ;header checksum
cmp.l d0,d5
bne .HeaderError
;Verify track position
swap d1
cmp.b TDU_CURRTRK+1(A_UNIT),d1
bne .WrongTrack
swap d1
bsr DecodeLong ;data area checksum
lea (a0,d1.w),a1 ;compute destination
;Decode and checksum data block
moveq #127,d6
moveq #0,d5
.L1: move.l 512(a2),d4
eor.l d4,d5
and.l d2,d4
move.l (a2)+,d1
eor.l d1,d5
and.l d2,d1
add.l d1,d1
or.l d1,d4
move.l d4,(a1)+
dbra d6,.L1
and.l d2,d5
lea 512(a2),a2
cmp.l d0,d5 ;check data area checksum
bne .DataError
.EndLoop: dbra d3,.SecLoop
.End: bsr StopDMA
movem.l (sp)+,d0-d6/a0-a2
rts
.HeaderError:
bsr Clear
moveq #DISK_BadHeader,d7
bra .End
.DataError:
bsr Clear
moveq #DISK_BadData,d7
bra .End
.WrongTrack:
bsr Clear
moveq #TDERR_SeekError,d7
move.w TDU_CURRTRK(A_UNIT),d3
bsr SeekZero
bsr SelectDrive
bsr Seek
bra .End
DecodeLong:
;A2 - ptr to buffer -- updated
;D2 - $55555555
;D0 - result
move.l d1,-(sp)
move.l (a2)+,d0
move.l (a2)+,d1
and.l d2,d0
and.l d2,d1
add.l d0,d0 ;was lsl.l #1,d0
or.l d1,d0
move.l (sp)+,d1
rts
EncodeLong:
;Enter with data to be encoded in D0.L
;and pointer to destination in A0 -- updated
;Exit with checksum in D5
movem.l d0-d4,-(sp)
moveq #0,d5
move.l #$55555555,d4
move.l d0,d3
lsr.l #1,d0
bsr Encode
move.l d3,d0
bsr Encode
and.l #$55555555,d5
movem.l (sp)+,d0-d4
rts
Encode:
;Enter with longword to code in D0.L and #$55555555 in D4
;uses d0,d1,d2,a0 -- not saved
;Accumulates checksum in D5
and.l d4,d0
move.l d0,d2
eor.l d4,d2
move.l d2,d1
add.l d2,d2
lsr.l #1,d1
bset #31,d1
and.l d2,d1
or.l d1,d0
btst #0,-1(a0)
beq .ok
bclr #31,d0
.ok: eor.l d0,d5
move.l d0,(a0)+
rts
EncodeBlock:
;Destination is always chip RAM (RawBuffer).
;Source could be in chip RAM or fast RAM (in A2).
movem.l d0-d1/a0-a1/a6,-(sp)
move.l 4,a6
move.l a2,a1
SYS TypeOfMem
and.l #MEMF_CHIP,d0
bne .Chip
movem.l (sp)+,d0-d1/a0-a1/a6
bra EncodeBlockCPU
.Chip: movem.l (sp)+,d0-d1/a0-a1/a6
bra EncodeBlockBlit
EncodeBlockCPU:
;Enter with pointer to source data in A2 -- updated
;Enter with pointer to destination in A0 -- updated
;Exit with checksum in D5
move.l d6,-(sp)
moveq #0,d5
move.w #(512/4)-1,d6
EncodeBlockSub:
;Number of longwords to encode (minus one) in D6.w
movem.l d0-d4,-(sp)
;Encode odd bits
push a2
move.w d6,d3
move.l #$55555555,d4
.L1: move.l (a2)+,d0
lsr.l #1,d0
bsr Encode
dbra d3,.L1
;Encode even bits
pop a2
move.w d6,d3
.L2: move.l (a2)+,d0
bsr Encode
dbra d3,.L2
and.l #$55555555,d5
movem.l (sp)+,d0-d4
move.l (sp)+,d6
rts
EncodeSectorLabels:
;D5 (checksum) must be initialized by caller
move.l d6,-(sp)
move.w #(16/4)-1,d6
bra EncodeBlockSub
EncodeBlockBlit:
;Enter with pointer to source data in A2 -- updated
;Enter with pointer to destination in A0 -- updated
;Exit with checksum in D5
;Extra blitter equates...
BLTCPTR equ $048
BLTBPTR equ $04c
BLTAPTR equ $050
BLTDPTR equ $054
movem.l d0-d2/a0-a1/a6,-(sp)
push a0
move.l GraphBase(A_DEVICE),a6
SYS OwnBlitter
move.l (sp),a0
move.l #_custom,a1
move.w #$808,d0 ;BLTSIZE
SYS WaitBlit
move.w #$ffff,BLTAFWM(a1)
move.w #$ffff,BLTALWM(a1)
clr.w BLTBMOD(a1)
clr.w BLTAMOD(a1)
clr.w BLTDMOD(a1)
move.w #$5555,BLTCDAT(a1)
move.l a2,BLTBPTR(a1)
move.l a2,BLTAPTR(a1)
move.l a0,BLTDPTR(a1)
move.w #$1db1,BLTCON0(a1)
clr.w BLTCON1(a1)
move.w d0,BLTSIZE(a1)
SYS WaitBlit
move.l a0,BLTBPTR(a1)
move.l a2,BLTAPTR(a1)
move.l a0,BLTDPTR(a1)
move.w #$2d8c,BLTCON0(a1)
move.w d0,BLTSIZE(a1)
movem.l a0/a2,-(sp)
lea 510(a2),a2 ;ptr to end of src
lea 1022(a0),a0
SYS WaitBlit
move.l a2,BLTBPTR(a1) ;src end
move.l a2,BLTAPTR(a1) ;src end
move.l a0,BLTDPTR(a1) ;dst end
move.w #$0db1,BLTCON0(a1)
move.w #$1002,BLTCON1(a1) ;decrement
move.w d0,BLTSIZE(a1)
movem.l (sp)+,a0/a2
lea 512(a0),a0
SYS WaitBlit
move.l a0,BLTBPTR(a1)
move.l a2,BLTAPTR(a1)
move.l a0,BLTDPTR(a1)
move.w #$1d8c,BLTCON0(a1)
clr.w BLTCON1(a1)
move.w d0,BLTSIZE(a1)
pop a0
SYS WaitBlit
bsr Correct
lea 512(a0),a0
bsr Correct
lea -512(a0),a0
move.w #(1024/4)-1,d0
move.l #$55555555,d2
moveq #0,d5
.. move.l (a0)+,d1
eor.l d1,d5
dbra d0,..
and.l d2,d5
SYS DisownBlitter
movem.l (sp)+,d0-d2/a0-a1/a6
lea 512(a2),a2 ;update source pointer
lea 1024(a0),a0 ;update destination pointer
rts
;This routine corrects the high bit of the current byte based on the
;low bit of the previous byte.
Correct:
push d0
move.b (a0),d0
btst #0,-1(a0)
bne .ResetClock
btst #6,d0
bne .end
bset #7,d0
bra .end1
.ResetClock:
bclr #7,d0
.end1: move.b d0,(a0)
.end: pop d0
rts
DISK_Wait:
;Assumes $dff000 in A6.
movem.l d0-d1,-(sp)
tst.w SyncCount(A_DEVICE)
bmi .OK ;if WaitWordSync detected a BlockSig, don't wait!
move.l BlockSig(A_DEVICE),d0
move.l #300*1000,d1
bsr TimeOutWait
bne .OK
moveq #DISK_NoSync,d7
.OK: move.w #INTF_DSKBLK+INTF_DSKSYNC,intena(a6) ;disable ints
bsr ClearSigs
movem.l (sp)+,d0-d1
rts
EncodeAndWriteTrack:
;Enter with pointer to source data in A2
movem.l d0-d6/a0-a2,-(sp)
btst #CIAB_DSKPROT,ciaapra ;check write protect status
beq .Protected
move.l RawBuffer(A_DEVICE),a0
;Gap = 1660 bytes - 2 bytes for hardware bug
move.l #$aaaaaaaa,d1 ;10101010...
move.w #414,d0
.. move.l d1,(a0)+
dbra d0,..
subq.l #2,a0 ;leave room for 2 extra bytes at the very end
moveq #11,d1 ;number of sectors
moveq #0,d3 ;sector count
.SecLoop:
move.l #$aaaaaaaa,(a0)
bsr Correct
addq.l #4,a0
move.l #$44894489,(a0)+
move.l #$ff000000,d0
moveq #0,d6
move.w TDU_CURRTRK(A_UNIT),d6
swap d6
or.l d6,d0
move.l d3,d6
lsl.l #8,d6
or.l d6,d0
or.l d1,d0
bsr EncodeLong ;header
;Encode sector label
push a2
lea SectorLabels(A_DEVICE),a2
move.l d3,d0
lsl.l #4,d0 ;sector*16
lea (a2,d0.w),a2
bsr EncodeSectorLabels
pop a2
move.l d5,d0
bsr EncodeLong ;header checksum
move.l a0,d2 ;save raw data pointer
addq.l #8,a0
bsr EncodeBlock ;encode data block
move.l d5,d0
exg a0,d2
bsr EncodeLong ;data block checksum
bsr Correct
move.l d2,a0
addq.l #1,d3
subq.l #1,d1
bne .SecLoop
move.w #$aaa8,(a0)
bsr Correct ;extra word to avoid hardware bug
;Physically write the data
.WriteAgain:
bsr SetRegs
bsr PreComp
move.w #ADKF_WORDSYNC,adkcon(a6) ;turn OFF wordsync!!!
move.w d7,dsksync(a6)
bsr ClearSigs
btst #TDPB_VERIFY,TDU_PUBFLAGS(A_UNIT)
beq .SkipVerify
bset #DEVB_Verify,DEV_FLAGS(A_DEVICE)
.SkipVerify:
move.l VerifyBuffer(A_DEVICE),a0
clr.l (a0)+
clr.l (a0)
move.w #0,dsklen(a6)
move.w #$DA9E,dsklen(a6)
move.w #$DA9E,dsklen(a6) ;write approx. 13,628 bytes
;This piece of code (commented out) tests the function of the rare
;"interrupt delayed" requester. (I've never seen it appear in actual use).
comment |
;TEST
push a6
move.l 4,a6
SYS Disable
move.w #5000,d0
.L1: move.b vhposr+_custom,d1
.L2: cmp.b vhposr+_custom,d1
beq .L2
dbra d0,.L1
SYS Enable
pop a6
|
btst #TDPB_VERIFY,TDU_PUBFLAGS(A_UNIT)
beq .NoVerify
;VERIFY
;We verify by comparing the raw MFM data in RawBuffer (what we just wrote)
;and VerifyBuffer (what is coming in). Due to the ingenious method of
;verifying (thanks to Sebastiano Vigna!) the data comes in sector-by-sector
;in the same order that we wrote it.
move.l VerifyBuffer(A_DEVICE),a0
move.l RawBuffer(A_DEVICE),a2
lea 1666(a2),a2 ;first sector (minus sync)
bsr WaitWordSync
tst.l d7
bne .VerifyError
bsr WaitWordSync
tst.l d7
bne .VerifyError
;Now we have our first sector in the verify buffer. Scan for a
;sync mark. (There may be 1 or 2 sync marks).
cmp.w #$4489,(a0)+
bne .VerifyError
cmp.w #$4489,(a0)
bne .HaveSync
addq.l #2,a0
.HaveSync:
;We go through a rather elaborate procedure here to make sure that we've
;started reading with sector 0. (If not, display a requester informing the
;user that something is locking out level-1 interrupts for a long period of
;time).
push a2
move.l #$55555555,d2
move.l a0,a2
bsr DecodeLong
pop a2
and.w #$ff00,d0
beq .Sector0
move.l a0,a1
moveq #9,d0
moveq #0,d1
.. move.l (a1)+,d3
eor.l d3,d1
dbra d0,..
and.l d2,d1
push a2
move.l a1,a2
bsr DecodeLong
pop a2
cmp.l d0,d1
bne .VerifyError
;Display an informational requester.
bsr StopDMA
push a6
move.l IntBase(A_DEVICE),a6
cmp.w #36,LIB_VERSION(a6)
bhi .KS20
;Running under 1.3. Put up a DisplayAlert.
moveq #0,d0 ;alert type (recoverable)
lea .AlertLockOut(pc),a0
moveq #20,d1 ;height
SYS DisplayAlert
pop a6
bra .WriteAgain
;Running under 2.0. Put up a EasyRequest.
.KS20:
sub.l a0,a0
sub.l a2,a2
lea .LockOut(pc),a1
SYS EasyRequestArgs
pop a6
bra .WriteAgain
;Note: Probably should compare with the blitter, but this will be fairly
;fast.
.Sector0:
move.w #270-1,d0
.. cmp.l (a0)+,(a2)+
dbne d0,..
bne .VerifyError
;Now we are over the initial hump of the first sync mark. The rest of the
;compare is even easier.
moveq #9-1,d1
.VLoop: bsr WaitWordSync
tst.l d7
bne .VerifyError
move.w #272-1,d0
.. cmp.l (a0)+,(a2)+
dbne d0,..
bne .VerifyError
dbra d1,.VLoop
;We have one more sector to verify. This time we must wait for "Block
;done", rather than another sync.
bsr DISK_Wait
tst.l d7
bne .VerifyError
move.w #272-1,d0
.. cmp.l (a0)+,(a2)+
dbne d0,..
bne .VerifyError
bra .End
.VerifyError:
moveq #0,d7 ;don't propagate the error to the app
;We go here if an error is detected during the verify. We first shut down
;the read operation that may be in progress, then put up a requester and let
;the user choose whether to retry or abort.
bsr StopDMA ;stop!!
push a6
move.l IntBase(A_DEVICE),a6
cmp.w #36,LIB_VERSION(a6)
bhi .DoKS20
;Running under 1.3. Put up a DisplayAlert.
moveq #0,d0 ;alert type (recoverable)
lea .AlertVError(pc),a0
moveq #20,d1 ;height
SYS DisplayAlert
;D0 is set to 'TRUE' if the LEFT button was pressed.
pop a6
tst.l d0
bne .WriteAgain ;go if left button pressed
bra .End
;Running under 2.0. Put up a EasyRequest.
.DoKS20: sub.l a0,a0
sub.l a2,a2
lea .VError(pc),a1
SYS EasyRequestArgs
pop a6
tst.l d0
bne .WriteAgain ;go if left gadget hit
bra .End
.NoVerify:
bsr DISK_Wait
move.l #3*1000,d0
bsr delay ;post-write delay
.End: movem.l (sp)+,d0-d6/a0-a2
rts
.Protected:
moveq #DISK_WriteProtected,d7
bra .End
.AlertVError:
dc.w 10 ;x coordinate
dc.b 10 ;y coordinate
dc.b '*** VERIFY ERROR !!! *** Hit LEFT button to RETRY'
dc.b ', or RIGHT button to CANCEL.',0
dc.b 0 ;continuation byte
even
.AlertLockOut:
dc.w 10 ;x coordinate
dc.b 10 ;y coordinate
dc.b 'Disk block interrupt delayed by >10ms.'
dc.b ' Press mouse button.',0
dc.b 0 ;continuation byte
even
.VError: dc.l es_SIZEOF
dc.l 0
dc.l .Title
dc.l .MainText
dc.l .GadgetText
.LockOut: dc.l es_SIZEOF
dc.l 0
dc.l .Title
dc.l .LockOutTxt
dc.l .Okay
.LockOutTxt:
dc.b 'Disk block interrupt delayed by >10ms caused erroneous'
dc.b ' verify.',0
.Okay: dc.b 'If you say so. Try it again!',0
.Title: dc.b 'hackdisk.device message',0
.MainText:
dc.b '*** VERIFY ERROR!!! ***',0
.GadgetText:
dc.b 'Retry|Cancel',0
even
DISK_Write:
;Error code returned in D7, as always.
movem.l d0-d5/a0-a2,-(sp)
btst #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
bne .ProtError
move.l d0,d5 ;length
;The meat of the write routine...
.WriteLoop:
move.l d1,d3 ;offset
divu.w d6,d3 ;d3.w is track #
move.l d3,d4
clr.w d4
swap d4 ;d4.l is byte offset into track
bsr MinSeek
tst.l d4 ;any offset?
bne .Complex ;yes
cmp.l d6,d5 ;at least a track left?
blo .Complex ;no
move.l a0,d0
btst #0,d0 ;word aligned?
bne .Complex ;no, do it the long way
move.l a0,a2
bsr EncodeAndWriteTrack
tst.l d7
bne .End
sub.l d6,d5 ;update length
beq .End
add.l d6,a0 ;update source pointer
add.l d6,d1 ;update offset
bra .WriteLoop
.Complex:
;This part is somewhat difficult. We check the offset and length parameters
;to see whether they're a multiple of 512. If so, we keep track of which
;sectors will be written in the buffer. This information is later used by
;Update to determine whether a part of the original track must be read in.
;(We don't attempt this optimization if the user is writing some odd number
;of bytes...This is probably why trackdisk has the limits that it does).
tst.l d5
beq .End ;nothing left, forget it
move.l d4,d0
and.w #%111111111,d0
bne .NoOpt
move.l d4,d0
move.l d5,d2
and.w #%111111111,d2
bne .NoOpt
move.l d5,d2
lsr.l #8,d0
lsr.l #1,d0 ;get starting sector number
lsr.l #8,d2
lsr.l #1,d2 ;get length in sectors
move.l WriteMap(A_DEVICE),d7
.OptLoop: bset d0,d7
addq.b #1,d0
cmp.b #32,d0
beq .EOpt
subq.l #1,d2
bne .OptLoop
.EOpt: move.l d7,WriteMap(A_DEVICE)
moveq #0,d7
;This is normally done by ReadTrackAndDecode.
move.b UnitNum(A_UNIT),BufferDrive(A_DEVICE)
move.b TDU_CURRTRK+1(A_UNIT),BufferTrack(A_DEVICE)
bra .Opt ;don't read (yet)
.NoOpt: push a0
move.l DecodedBuffer(A_DEVICE),a0
bsr ReadTrackAndDecodeBuffered
pop a0
tst.l d7
bne .End
.Opt: move.l DecodedBuffer(A_DEVICE),a1
add.l d4,a1 ;add byte offset into track
sub.l d6,d4
neg.l d4
add.l d4,d1 ;update offset
;D4.L - number of bytes that could be transferred from this track
;D5.L - number of bytes left in the entire Read request
cmp.l d5,d4
bhs .FinalWrite
sub.l d4,d5 ;update length
move.l d4,d0
bsr CopyMem
add.l d0,a0 ;update dest pointer
bset #DEVB_Dirty,DEV_FLAGS(A_DEVICE)
bra .WriteLoop
.FinalWrite:
move.l d5,d0
bsr CopyMem
bset #DEVB_Dirty,DEV_FLAGS(A_DEVICE)
.End: movem.l (sp)+,d0-d5/a0-a2
rts
.ProtError:
moveq #DISK_WriteProtected,d7
bra .End
MotorOff:
;MotorOff turns off all drive motors and leaves all drives deselected
bsr Deselect
bset #CIAB_DSKMOTOR,ciabprb ;motor off
and.b #~(CIAF_DSKSEL0+CIAF_DSKSEL1+CIAF_DSKSEL2+CIAF_DSKSEL3),ciabprb ;select all drives
bsr Deselect
clr.b MotorState(A_DEVICE)
rts
Inquire:
IFND _LVOGetUnitID
_LVOGetUnitID equ -30
ENDC
movem.l d0-d2/a0-a1/a6,-(sp)
moveq #0,d2
move.l DiskResourceBase(A_DEVICE),a6
.Loop: move.l d2,d0
SYS GetUnitID
tst.l d0
bne .Next
bset d2,InquireBits(A_DEVICE)
.Next: addq.l #1,d2
cmp.w #MD_NUMUNITS,d2
blo .Loop
movem.l (sp)+,d0-d2/a0-a1/a6
rts
comment |
;Find out what drives are in the system
;Exit with "drive map" in D0 (bit 0 = drive 0, bit 1 = drive 1, etc.)
;See the Hardware Reference Manual, Appendix E for a description of
;the procedure.
;Note: Drive zero is always present. In fact, the identification
;scheme does not work with drive zero (except for the half-speed drive).
Inquire:
movem.l d1-d5/a0,-(sp)
moveq #4,d3
moveq #1,d2 ;drive count
moveq #1,d0 ;drive map (drive zero always present)
moveq #7,d1
move.l #ciabprb,a0
bsr Deselect
.InquireLoop:
moveq #31,d4
moveq #0,d5 ;identification longword
bclr d1,(a0) ;motor on
bset d3,(a0) ;deselect drive
bclr d3,(a0) ;select drive
bset d1,(a0) ;motor off
bset d3,(a0) ;deselect drive
bclr d3,(a0) ;select drive
bset d3,(a0) ;deselect drive
.ReadIdent:
bclr d3,(a0) ;select drive
btst #5,ciaapra ;test ready
beq .zero
bset d4,d5
.zero: bset d3,(a0) ;deselect drive
dbra d4,.ReadIdent
tst.l d5
bne .Skip
bset d2,d0
.Skip: addq.l #1,d3
addq.l #1,d2
cmp.b #4,d2
bne .InquireLoop
movem.l (sp)+,d1-d5/a0
rts
|
SeekZeroAll:
movem.l d0-d2/A_UNIT,-(sp)
move.b InquireBits(A_DEVICE),d0
lea Unit0(A_DEVICE),A_UNIT
moveq #0,d1
moveq #MD_NUMUNITS-1,d2
.Loop: btst d1,d0
beq .Next
bsr SeekZero
.Next: lea MyUnit_Sizeof(A_UNIT),A_UNIT
addq.b #1,d1
dbra d2,.Loop
movem.l (sp)+,d0-d2/A_UNIT
rts
SeekZero:
;Places the drive in UnitNum(A_UNIT) on track zero.
;Drive does not need to be selected in advance.
;Drive will be left _deselected_!
movem.l d0-d1,-(sp)
.StepLoop:
bsr SelectDriveSameMotor
bset #CIAB_DSKDIREC,ciabprb ;set to "out" (lower tracks)
btst #CIAB_DSKTRACK0,ciaapra ;check track zero flag
beq .EndStepLoop
bset #CIAB_DSKSTEP,ciabprb
bclr #CIAB_DSKSTEP,ciabprb ;step head
bset #CIAB_DSKSTEP,ciabprb
bsr Deselect
move.l TDU_CALIBRATEDELAY(A_UNIT),d0
bsr delay
bra .StepLoop
.EndStepLoop:
bsr Deselect
move.l TDU_SETTLEDELAY(A_UNIT),d0
bsr delay
clr.w TDU_CURRTRK(A_UNIT)
movem.l (sp)+,d0-d1
rts
CopyMemSlow:
;Only to be called by CopyMem
move.l 4,a6
SYS CopyMem
movem.l (sp)+,d0-d7/a0-a6
rts
CopyMem:
;A0 - source
;A1 - destination
;D0 - size
movem.l d0-d7/a0-a6,-(sp)
move.l a0,d1
btst #0,d1
bne CopyMemSlow
move.l a1,d1
btst #0,d1
bne CopyMemSlow
.More: cmp.l #512,d0
blo CopyMemSlow
;Copy 480 bytes
n set 0
REPT 10
movem.l (a0)+,d1-d7/a2-a6
movem.l d1-d7/a2-a6,n*48(a1)
n set n+1
ENDR
;Copy 32 bytes
movem.l (a0)+,d1-d7/a2
movem.l d1-d7/a2,480(a1)
lea 512(a1),a1
sub.l #512,d0
bne .More
movem.l (sp)+,d0-d7/a0-a6
rts
;Debugging stuff
ifne INFO_LEVEL ;If any debugging enabled at all
KPutFmt: move.l a2,-(sp)
lea KPutChar(pc),a2
bsr KDoFmt
move.l (sp)+,a2
rts
KDoFmt: move.l a6,-(sp)
move.l 4,a6
SYS RawDoFmt
move.l (sp)+,a6
rts
KPutChar:
;Serial
comment |
move.l a6,-(sp)
move.l 4,a6
SYS RawPutChar
move.l (sp)+,a6
rts
|
;Printer
comment |
move.b #$ff,$bfe301
.Print: btst #0,$bfd000
bne .Print
move.b d0,$bfe101
rts
|
;Memory
tst.l MemPtr
bne .OK
move.l #$500000,MemPtr
.OK:
push a0
move.l MemPtr(pc),a0
move.b d0,(a0)+
move.l a0,MemPtr
pop a0
rts
MemPtr: dc.l 0
endc
EndCode: